package com.guanjialhx.controller;

import com.alibaba.fastjson.JSON;
import com.guanjialhx.pojo.Apply;
import com.guanjialhx.pojo.WeiXin;
import com.guanjialhx.config.WeiXinConfig;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 微信支付的接口
 * 微信退款的接口
 */
@RestController
@CrossOrigin
@RequestMapping(value = "/WeiXinPay")
public class WeiXinPayController {
    //日志打印
    private final Logger log = LoggerFactory.getLogger(WeiXinPayController.class);
    //居家照护管家公众号appid
    private String appid = "wxcb64d81519996b30";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 访问启效云的参数
     */
    private static final String appKey = "d3aa45bbd1e94d70b5fe59b1fa37da21";
    private static final String appSecret = "883fbc209be344e899544af7c7caa0fc";
    private static final String qxUrl = "https://changhu.qixiaocloud.com";

    /**
     * 微信支付的接口
     *
     * @return
     */
    @PostMapping("WxPay")
    public WeiXin WxPay(@RequestBody Apply apply) {
        log.info("进入预支付的接口获取参数！！！！！！");
        log.info("apply = " + apply);
        System.out.println();
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(WeiXinConfig.MERCHANT_ID)
                .privateKeyFromPath(WeiXinConfig.MERCHANT_PRIVATE_KEY_PATH)
                .merchantSerialNumber(WeiXinConfig.MERCHANT_SERIAL_NUMBER)
                .apiV3Key(WeiXinConfig.API_V3_KEY)
                .build();
        //获取预支付订单号
        //构建service
        JsapiService service = new JsapiService.Builder().config(config).build();
        //request.setXxx(val)设置所需参数，具体参数可见Request定义,参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        //设置金额, 以分为单位 金额根据实际情况自行填写
//        int money = (apply.getBmtype().equals("1790")) ? 179000 : 199000;
//        amount.setTotal(money);
        amount.setTotal(199000);//上线
//        amount.setTotal(100);//测试
        amount.setCurrency("CNY");
        request.setAmount(amount);
        //绑定微信支 公众号的appid 根据实际情况自行获取填写
        request.setAppid(appid);
        // 商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一,根据自己需求自己定义
        request.setMchid(WeiXinConfig.MERCHANT_ID);
        request.setDescription(apply.getBmtype().equals("1990") ? "非上海户籍" : "上海户籍");
        //支付成功后回调的URL
        request.setNotifyUrl(WeiXinConfig.PAY_NOTIFY_URL);
        //商家订单号
        log.info("商家订单号：" + UUID.randomUUID().toString().replace("-", ""));
        System.out.println();
        request.setOutTradeNo(UUID.randomUUID().toString().replace("-", ""));
        //自定义信息, 在支付订单回调可以取到(自行替换,如不需要可注释掉)
        Map meetIfno = new HashMap();
        meetIfno.put("bmName", apply.getBmName());
        meetIfno.put("bmTel", apply.getBmTel());
        meetIfno.put("bmUID", apply.getBmUID());
        if (apply.getBmtype().equals("1990")) {
            apply.setBmtype("9");
        }
        if (apply.getBmtype().equals("1790")) {
            apply.setBmtype("7");
        }
        meetIfno.put("bmtype", apply.getBmtype());
        meetIfno.put("bmid", apply.getBmid());     //创客老师的创客编号id     c端报名支付的时候是没有这个值的
        meetIfno.put("lsname", apply.getLsname()); //创客老师的名称          c端报名支付的时候是没有这个值的
//        meetIfno.put("bmUIDType",apply.getBmUIDType() );//默认身份证
//        meetIfno.put("bmSex",apply.getBmSex());         //可根据身份证计算
//        meetIfno.put("bmdateBirth",apply.getBmdateBirth() );  //可以根据身份证计算
//        meetIfno.put("openid",apply.getOpenid());  //回调那边有
        request.setAttach(JSON.toJSONString(meetIfno));
        //支付人员的的openid
        Payer payer = new Payer();
        payer.setOpenid(apply.getOpenid());
//        payer.setOpenid("o3FTe6dBzidn4Qihh8kIC-ht3ggs");
        request.setPayer(payer);
        //调用支付接口
        PrepayResponse response = service.prepay(request);
        /**
         * 响应体中有预支付订单号
         */
        String prepayId = response.getPrepayId();
        log.info("预支付Prepay_Id：" + prepayId);
        System.out.println();
        //以上为获取预支付订单号步骤
        /**
         * 下面为使用加密获取密钥,也需要返回给前段
         */
        long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        //构造签名参数(appid更换为实际支付人员的appid)
        String message = "appid" + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + response.getPrepayId() + "\n";
        //生成签名
//        Signer rsaSigner = new RSASigner(WeiXinConfig.MERCHANT_SERIAL_NUMBER, WeiXinConfig.MERCHANT_PRIVATE_KEY);
//        String paySign  = rsaSigner.sign(message).getSign();
        String paySign = null;
        try {
            // 签名
            StringBuilder sb = new StringBuilder();
            // 应用id
            sb.append(appid).append("\n");
            // 支付签名时间戳
            sb.append(timeStamp).append("\n");
            // 随机字符串
            sb.append(nonceStr).append("\n");
            // 预支付交易会话ID
            sb.append("prepay_id=").append(prepayId).append("\n");
            Signature sign = Signature.getInstance("SHA256withRSA");
            PrivateKey privateKey = WeiXinConfig.MERCHANT_PRIVATE_KEY;
            sign.initSign(privateKey);
            sign.update(sb.toString().getBytes("utf-8"));
            paySign = Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //前端通过该签名可以调用支付
        log.info("签名数据paySign：" + paySign);
        System.out.println();
        /**
         * 注意:
         * 1.预支付订单号(prepay_id),以及生成签名的timeStamp,nonceStr的参数都需要返回前端
         * 2.在构造签名参数的时候,\n一定要加 否则也是签名无效
         */
        WeiXin weiXin = new WeiXin();
        weiXin.setOpenid(apply.getOpenid());
        weiXin.setAppId(appid);
        weiXin.setPaySign(paySign);
        weiXin.setTimeStamp(timeStamp);
        weiXin.setPrepayId(prepayId);
        weiXin.setNonceStr(nonceStr);
        log.info("传给前端的参数 = " + weiXin);
        return weiXin;
    }

    //装换为分为单位
    public static int convertToCents(String amountInDollars) {
        double amount = Double.parseDouble(amountInDollars); // 将字符串转换为双精度浮点数
        int cents = (int) (amount * 100); // 将金额乘以100，然后转换为整数
        return cents;
    }

    /**
     * 微信退款申请
     *
     * @param TransactionId 支付单号
     * @param OutRefundNo   商户订单号
     * Refund        【退款金额】 退款标价金额，单位为分，可以做部分退款
     * Total         【订单金额】 订单总金额，单位为分
     * @return
     */
    @GetMapping("WxRefund")
    public String Wx(String TransactionId, String OutRefundNo) {
        log.info("TransactionId:"+TransactionId);
        log.info("OutRefundNo:"+OutRefundNo);
        try {
            /**
             * 根据商户订单号和订单号到启效云查询对应数据的总金额和退费金额
             */
            String qxToken = this.token();
            Map<String, String> headMap = new HashMap<>();
            Map param = new HashMap();
            String url = "https://changhu.qixiaocloud.com/open/qx-apaas-lowcode/gj/SelectUserRefund";
            headMap.put("Authorization", qxToken);
            param = new HashMap();
            param.put("TransactionId", TransactionId);
            param.put("OutRefundNo", OutRefundNo);
            String qxAdd = WeChatPayInfoController.postJsonParam(url, param, headMap);
            net.sf.json.JSONObject qxAddJJson = net.sf.json.JSONObject.fromObject(qxAdd);
            net.sf.json.JSONObject data = net.sf.json.JSONObject.fromObject(qxAddJJson.getString("data"));
            String RefundString = (String) data.get("Refund");
            String TotalString = (String) data.get("Total");
            int Refund = convertToCents(RefundString);
            int Total = convertToCents(TotalString);
            System.out.println("Refund = " + Refund);
            System.out.println("Total = " + Total);
            // 使用自动更新平台证书的RSA配置
            // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
            Config config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(WeiXinConfig.MERCHANT_ID)
                    .privateKeyFromPath(WeiXinConfig.MERCHANT_PRIVATE_KEY_PATH)
                    .merchantSerialNumber(WeiXinConfig.MERCHANT_SERIAL_NUMBER)
                    .apiV3Key(WeiXinConfig.API_V3_KEY)
                    .build();
            //构造申请退款对象
            RefundService service = new RefundService.Builder().config(config).build();
            //请求参数
            CreateRequest request = new CreateRequest();
            //设置退款金额 根据自己的实际业务自行填写
            AmountReq amountReq = new AmountReq();
            amountReq.setRefund((long) Refund); //【退款金额】 退款标价金额，单位为分，可以做部分退款
            amountReq.setTotal((long) Total);  // 【订单金额】 订单总金额，单位为分
            amountReq.setCurrency("CNY"); //【退款币种】 符合ISO 4217标准的三位字母代码，目前只支持人民币：CNY。
            request.setAmount(amountReq);
            //支付成功后回调回来的transactionId 支付单号 按照实际情况填写
            request.setTransactionId(TransactionId);
            //支付成功后回调回来的OutRefundNo  商户订单号 按照实际情况填写
            request.setOutRefundNo(OutRefundNo);
            //退款成功的回调地址
            request.setNotifyUrl(WeiXinConfig.REFUND_NOTIFY_URL);
            //发起请求,申请退款
            com.wechat.pay.java.service.refund.model.Refund refund = service.create(request);
            //调用成功的响应数据
            log.info(String.valueOf(refund));
            /**
             * 注意:
             * 1.以上参数都需要按照实际情况填写,大部分参数都是付款成功之后的回调数据
             * 2.以上退款是必要参数,如果想要填写其他参数参考文档:https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/create.html
             * 3.SDK的微信支付的退款代码参考:https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/service/src/main/java/com/wechat/pay/java/service/refund/RefundService.java
             */
        } catch (Exception e) {
            e.printStackTrace();
            log.info("错误信息："+String.valueOf(e));
            return String.valueOf(e);
        }
        log.info("退款成功!");
        return "退款成功!";
    }

    /**
     * 生成访问启效云的token
     */
    public String token() {
        try {
            String url = qxUrl + "/open/oauth/token";
            Map<String, String> headMap = new HashMap<>();
            Map param = new HashMap();
            param.put("appKey", appKey);
            param.put("appSecret", appSecret);
            String qxLogin = null;
            qxLogin = WeChatPayInfoController.postJsonParam(url, param, headMap);
            log.info("qxLogin = " + qxLogin);
            net.sf.json.JSONObject qxLoginJson = net.sf.json.JSONObject.fromObject(qxLogin);
            String success = qxLoginJson.getString("success");
            if ("true".equals(success)) {
                net.sf.json.JSONObject qxData = net.sf.json.JSONObject.fromObject(qxLoginJson.getString("data"));
                String qxToken = qxData.getString("access_token");
                //将token存到redis设置全局有效时间 10分钟
                log.info("qxToken = " + qxToken);
                return qxToken;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

